module LoanManager (insertLoan, returnLoan, updateLoan, getByPeriod, getTop, FilmTop(FilmTop)) where

import Date
import DBS
import Loan
import IdObject
import Film
import Client
import qualified FilmManager

-- id klienta, id filmu, data wypozyczenia, data zwrotu planowana, DBS
insertLoan :: ID -> ID -> Date -> Date -> DBS -> Either DBS String
insertLoan ci fi dateL dateO db	=
   if dateL > dateO then Right "data wypozyczenia nie moze byc pozniejsza od daty 'do zwrotu'"
   else if (client /= Nothing) then
		case film of
                        Nothing -> Right "nie znaleziono filmu o podanym id"
			Just f	-> 
                                if (getState f /= Available)
                                then Right "film jest juz wypozyczony albo jest zgubiony"
                                else case maybeFilmUpdated of
                                        Left dbb2  -> Left (DBS.setLastLoanId i dbb2)
                                        Right msg  -> Right ("nie udalo sie zaktualizowac stanu filmu - " ++ msg)
                                 where  dbb = setLoans oss db
                                        os = getLoans db
                                        o = Loan i (ci, fi, dateL, dateO, Date.Empty)
                                        oss = os ++ [o]
                                        i = 1 + getLastLoanId db
                                        maybeFilmUpdated = (FilmManager.updateFilm fi dbb 5 "2")
	else Right "nie znaleziono klienta o podanym id"
	where	film = IdObject.get fi (getFilms db)
		client = IdObject.get ci (getClients db)

-- id filmu do zwrotu DBS
returnLoan :: ID -> Date.Date -> DBS.DBS -> Either DBS.DBS String
returnLoan fi d db	| film == Nothing			= Right "nie ma filmu o takim id"			
			| loans == []				= Right "ten film nie byl nigdy wypozyczony"
			| loans2 == []				= Right "ten film nie byl obecnie wypozyczony"
                        | (Loan.getDateLoaned loan) > d         = Right "data zwrocenia nie moze byc wczesniejsza niz data wypozyczenia" 
			| otherwise				= Left (DBS.setLoans loans3 db2)
			where	film = IdObject.get fi (DBS.getFilms db)
				loans = DBS.selectLoanBy ((== fi).getFilmId) db
				loans2 = filter ((== Empty).getDateReturned) loans
				loan = head loans2
				otherLoans = DBS.selectLoanBy ((/= (getId loan)).getId) db
				loans3 = (setDateReturned loan d) : otherLoans 
				db2 = case (FilmManager.updateFilm (getFilmId loan) db 5 "1") of -- zmiana stanu filmu na dostepny
					Left d	-> d

-- id filmu, id klienta, nr parametru daty, data, DBS
updateLoan :: ID -> ID -> Int -> Date -> DBS -> Either DBS String
updateLoan ci fi pi d db	| (pi /= 2 && pi /= 1)			= Right "nr parametru nie spoza zakresu"
				| length loans2 == 0			= Right "nie ma niezwroconego wypozyczenia dla tego filmu i klienta"
                                | (Loan.getDateLoaned loan2) > (Loan.getDateObliged loan2)                          
                                                                        = Right "data 'do zwrotu' nie moze byc wczesniejsza niz data wypozyczenia"
				| otherwise				= Left (setLoans ls2 db)				
				where	loans = filter (((== ci).getClientId)) (selectLoanBy ((== fi).getFilmId) db)			
					loans2 = filter ((== Empty).getDateReturned) loans
					loan = head loans2
					li = getId loan
					ls = selectLoanBy ((/= li).getId) db
					ls2 = ls ++ [loan2]
					loan2 = if pi == 1 then setDateLoaned loan d
						else            setDateObliged loan d					

getByPeriod :: Date.Date -> Date.Date -> DBS.DBS -> Either [(Loan.Loan, (Film.Film, Client.Client))] String
getByPeriod dateFrom dateTo db = 
        if dateFrom > dateTo then Right "Data 'od' nie moze byc pozniejsza od daty 'do'"
        else if loans == [] then Right "Nie znaleziono wypozyczen w podanym okresie"
        else Left (zip loans (zip films clients))
                where   loans = DBS.selectLoanBy fDate db
                        fDate = (\x -> ((Loan.getDateLoaned x) >= dateFrom && (Loan.getDateLoaned x) < dateTo)
                                || ((Loan.getDateReturned x) >= dateFrom && (Loan.getDateReturned x) < dateTo) )
                        films = map (\x -> DBS.selectFilm x db) filmIds
                        filmIds = map (Loan.getFilmId) loans
                        clients = map (\x -> DBS.selectClient x db) clientIds
                        clientIds = map (Loan.getClientId) loans

data FilmTop = FilmTop Film.Film Int

instance Eq FilmTop where
        (FilmTop _ score1) == (FilmTop _ score2) = score1 == score2

instance Ord FilmTop where
        (FilmTop _ score1) < (FilmTop _ score2) = score1 < score2
        (FilmTop _ score1) > (FilmTop _ score2) = score1 > score2
        (FilmTop _ score1) <= (FilmTop _ score2) = score1 <= score2
        (FilmTop _ score1) >= (FilmTop _ score2) = score1 >= score2

getTop :: Date.Date -> Date.Date -> Int -> DBS.DBS -> Either [FilmTop] String
getTop dateFrom dateTo topSize db = 
        if dateFrom > dateTo then Right "Data 'od' nie moze byc pozniejsza od daty 'do'"
        else if loans == [] then Right "Nie znaleziono wypozyczen w podanym okresie"
        else Left (take topSize (qsort (countFilms (qsort films))))
                where   loans = DBS.selectLoanBy fDate db
                        fDate = (\x -> ((Loan.getDateLoaned x) >= dateFrom && (Loan.getDateLoaned x) < dateTo) )
                        films = map (\x -> DBS.selectFilm x db) filmIds
                        filmIds = map (Loan.getFilmId) loans

countFilms :: [Film.Film] -> [FilmTop]
countFilms [] = []
countFilms films = (FilmTop (head films) len) : (countFilms (drop len films))
        where len = length (takeWhile (`sameFilm` (head films)) films)


qsort :: (Ord a) => [a] -> [a]
qsort [] = []
qsort (x:xs) = qsort (filter (> x) xs)
                ++ [x]
                ++ qsort (filter (<= x) xs)
